#include "besierspline.h"
#include <QPainter>
#include<QPainterPath>
#include<QMouseEvent>
#include<QEvent>
#include<QDebug>
#include<QFrame>


Eigen::Vector2f Evaluate(std::vector<Eigen::Vector2f> A, double u)
{
    assert(A.size()>1);
    int n=A.size()-1;
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<n-i;++j)
            A[j] = (1.0-u)*A[j]+u*A[j+1];
    }
    return A[0];
}


/*!
 *\brief Bezier曲线的打断
*\ param const std::vector<Point> & A 控制点序列
*\ param double u 打断处的参数 (0,1)
*\ param std::vector<Point> & cv_left 打断后前半部分的控制点
*\ param std::vector<Point> & cv_right 打断后后半部分的控制点
*\ Returns:   std::pair<List2f, List2f>
*/
std::pair<List2f, List2f> subDevide(const std::vector<Eigen::Vector2f> &A, float u)
{
    List2f cv_left = A;
    List2f cv_right = A;
    if(A.size()<2)return {cv_left, cv_right};

    int p = A.size()-1;
    //p次迭代
    for (int i = 0;i<p;++i)
    {
        for (int j = 0;j<p - i;++j)
            cv_right[j] = (1.0 - u)*cv_right[j] + u*cv_right[j + 1];
        //左侧控制点为每次迭代的首点
        cv_left[i + 1] = cv_right[0];
    }

    return {cv_left, cv_right};
}


besierspline::besierspline(QWidget *parent) : QLabel(parent), state(1)
{
    setGeometry(0,43, parent->width(), parent->height()-43);
    show();
    //this->setFrameShape(Shape::Box);
}

void besierspline::enterEvent(QEvent *event)
{
    qDebug()<<"鼠标进入了";
    //this->setFrameShape(Shape::Box);
    //this->setStyleSheet("border-width: 1px;border-style: solid;border-color: rgb(255, 170, 0);");
    //QLabel::enterEvent(event);
}

void besierspline::leaveEvent(QEvent *event)
{
    qDebug()<<"鼠标离开了";
    //this->setFrameShape(Shape::NoFrame);
    //this->setStyleSheet("");
    //QLabel::leaveEvent(event);
}

void besierspline::mouseMoveEvent(QMouseEvent *ev)
{    
    QLabel::mouseMoveEvent(ev);
    QString str1 = QString("鼠标移动了 drag=%1")
            .arg(state);
    qDebug()<<str1;
    if(state==1){// 新建，点跟着鼠标移动
        controlPoints.back() = Eigen::Vector2f( ev->x(),ev->y());
        update();
        return;
    }
    if(!drag)return;
     str1 = QString("鼠标移动了 drag=%1")
            .arg(drag);
    //qDebug()<<str1;
    QString str = QString("鼠标移动了 x=%1, y=%2, globalX=%3, globalY=%4" )
            .arg(ev->x()).arg(ev->y())
            .arg(ev->globalX()).arg(ev->globalY());
    //qDebug()<<str;
    auto movement = ev->globalPos()-lastPos;
        lastPos = ev->globalPos();

        this->move(mapToParent( movement));
        update();
}

void besierspline::mousePressEvent(QMouseEvent *ev)
{
    if(ev->button()==Qt::MouseButton::LeftButton) {
        if(state==1){
            controlPoints.push_back(Eigen::Vector2f( ev->x(),ev->y()));
            update();
            return;
        }
        emit select(this);
        lastPos = ev->globalPos();
        drag=1;
    }else if(ev->button()==Qt::RightButton) {
        if(state==1) {
            state=0;
            updateBox();
        }
    }
}

void besierspline::mouseReleaseEvent(QMouseEvent *ev)
{
    if(ev->button()==Qt::MouseButton::LeftButton) {
        if(state==1) {
            // 新建添加点
            controlPoints.back() = Eigen::Vector2f( ev->x(),ev->y());
            update();
            return;
        }
    }
}

std::pair<List2f, List2f> besierspline::getSplit(float u)
{
    std::pair<List2f, List2f> lr = subDevide(controlPoints, u);
    auto tl = geometry().topLeft();
    for(int i=0;i<lr.first.size();++i) {
        lr.first[i].x() += tl.x();
        lr.first[i].y() += tl.y();
        lr.second[i].x() += tl.x();
        lr.second[i].y() += tl.y();
    }
    return lr;
}

List2f besierspline::degreeElevation()
{
    auto de = ::degreeElevation(controlPoints);
    auto tl = geometry().topLeft();
    for(int i=0;i<de.size();++i) {
        de[i].x() += tl.x();
        de[i].y() += tl.y();
    }

    return de;
}

int besierspline::setControlPoints(const List2f &A)
{
    controlPoints=A;
    this->setGeometry(0,0, 10, 10);
    state = 0;
    updateBox();
}

void besierspline::updateBox()
{
    if(controlPoints.empty())return;
    auto tl = this->geometry().topLeft();
    for(auto &p: controlPoints){
       p.x()+= tl.x();
       p.y() += tl.y();
    }
    QRect rect(controlPoints[0].x(), controlPoints[0].y(), 2, 2);

    for(auto &p: controlPoints){
        if(p.y()< rect.top()) rect.setTop(p.y());
        if(p.y()> rect.bottom()) rect.setBottom(p.y());
        if(p.x()< rect.left()) rect.setLeft(p.x());
        if(p.x()> rect.right()) rect.setRight(p.x());
    }
    int offset = 10;
    rect.setTopLeft( rect.topLeft() - QPoint(offset, offset));
    rect.setBottomRight( rect.bottomRight() + QPoint(offset, offset));
    this->setGeometry(rect);
    tl = this->geometry().topLeft();
    for(auto &p: controlPoints){
        p.x()-=tl.x();
        p.y() -= tl.y();
    }
    update();
}

//绘图事件
void besierspline::paintEvent(QPaintEvent *event){
    QWidget::paintEvent(event);
    // qDebug()<<"画画";
    QPainter painter(this);
    QPen pen;

    // 画点
    pen.setWidth(10);
    pen.setColor(Qt::red);
    painter.setPen(pen);
    for(auto &p:controlPoints) {
        painter.drawPoint(QPoint(p.x(), p.y()));
    }

    pen.setColor(Qt::black);
    pen.setWidth(1);
    pen.setStyle(Qt::SolidLine);
    painter.setPen(pen);
    auto blines = getSpline(controlPoints, 0.01);
    for(int i=1;i<blines.size();++i) painter.drawLine(QPoint(blines[i-1].x(), blines[i-1].y()), QPoint(blines[i].x(), blines[i].y()));

    pen.setColor(Qt::blue);
    pen.setStyle(Qt::DashLine);
    painter.setPen(pen);

    for(int i=1;i<controlPoints.size();++i) {
        painter.drawLine(QPoint(controlPoints[i-1].x(), controlPoints[i-1].y()), QPoint(controlPoints[i].x(), controlPoints[i].y()));
    }
}

std::vector<Eigen::Vector2f> getSpline(std::vector<Eigen::Vector2f> &A, double step)
{
    std::vector<Eigen::Vector2f> res;
    if(A.size()<2)return res;
    for(double i=0;i<1;i+=step) res.push_back(Evaluate( A, i));
    return res;
}

List2f degreeElevation(const std::vector<Eigen::Vector2f> &A)
{
    int n=A.size(); // 为公式中的n+1
    List2f res (n+1);
    res.front() = A[0];
    res.back() = A.back();

    for(int i=1;i<n;++i) {
        float ratio = 1.0*i/n;
        res[i] = ratio *A[i-1] + (1-ratio)*A[i];
    }

    return res;
}
